import re

bottom = "bottom"  # artifricial bottomalue to avoid the need to know the set of used expressions
top = set()
iota = set()

def join(a, b):
    if a == bottom:
        return b
    elif b == bottom:
        return a
    else: return a | b

def meet(a, b):
    if a == bottom:
        return bottom
    elif b == bottom:
        return bottom
    else: return a & b
    
def args(expr_tree):
    if len(expr_tree) == 0:
        return
    if is_var(expr_tree[0]):
        yield expr_tree[0]
    for subtree in expr_tree[1]:
        for children in args(subtree):
            yield children
                     
def assign(live, lhs, e):
    return (live - set([lhs])) | set(args(e))
             
def no_assign(live, e):
    return live | set(args(e))

def is_var(literal):
    return re.match("^[_a-z][_a-z0-9]*$", literal) is not None
